﻿using System;

namespace Arch.Common.Reflection
{
    public static class ObjectExtentions
    {
        public static object[] EmptyArgs = new object[0];

        /// <summary>
        /// Invoke a method of an object using reflection
        /// </summary>
        /// <param name="obj">Target object</param>
        /// <param name="method">Name of the method</param>
        /// <param name="args">List of the arguments</param>
        /// <returns>The result of the method</returns>
        public static object call(this object obj, string method, params object[] args)
        {
            var type = obj.GetType();
            int argLength = args.Length;

            if (argLength > 0)
            {
                var argTypes = new Type[argLength];

                for (int i = 0; i < argLength; i++)
                {
                    argTypes[i] = args[i].GetType();
                }
                var invokeMethod = type.GetMethod(method, argTypes);

                //If the method with these type is not found try the first one
                if (invokeMethod == null) invokeMethod = type.GetMethod(method);

                //TODO : better get of the method

                return invokeMethod.Invoke(obj, args);
            }
            else
            {
                return type.GetMethod(method, new Type[0]).Invoke(obj, args);
            }
        }

        public static object get(this object obj, string property, object defaultValue = null)
        {
            var split = property.Split('.');
            object current = obj;
            foreach (var prop in split)
            {
                if (current == null)
                    return defaultValue;

                var type = current.GetType();
                current =  type.GetProperty(prop).GetGetMethod().Invoke(obj, ObjectExtentions.EmptyArgs);
            }
            return current;
        }

        public static T get<T>(this object obj, string property, T defaultValue = default(T))
        {
            return (T)get(obj, property, (object)defaultValue);
        }

        public static void set(this object obj, string propertyName, object value)
        {
            var type = obj.GetType();
            type.GetProperty(propertyName).GetSetMethod().Invoke(obj, new object[] { value });
        }
    }
}
